package strain.domain.interval_set;

public class Bound implements Comparable<Bound>{
	
	public static Bound LOWEST  = new Bound(Double.NEGATIVE_INFINITY, Dir.LOWER, true);
	public static Bound HIGHEST = new Bound(Double.POSITIVE_INFINITY, Dir.UPPER, true);
//------------------------------------------------------------------------------
	static enum Dir{
		LOWER, UPPER;
		
		private Dir opposite;

		static {
			LOWER.opposite = UPPER;
			UPPER.opposite = LOWER;
		}
		
		public Bound.Dir opposite(){
			return opposite;
		}
	}
//------------------------------------------------------------------------------
	private double  value;
	private Dir     dir;
	private boolean closed;
//------------------------------------------------------------------------------
	public Bound(double value, Bound.Dir dir, boolean closed){
	    assert(!Double.isNaN(value));
		this.value  = value;
		this.dir    = dir;
		this.closed = closed;
	}
	
	public Bound(double value, Bound.Dir dir){
		this(value, dir, true);
	}
//------------------------------------------------------------------------------
	public double value(){
		return value;
	}
	
	public Bound.Dir dir(){
		return dir;
	}
	
	public boolean closed(){
		return closed;
	}

	public boolean open(){
		return !closed;
	}
	
	public boolean isLower(){
		return (dir == Dir.LOWER);
	}
	
	public boolean isUpper(){
		return (dir == Dir.UPPER);
	}
//------------------------------------------------------------------------------
	public int compareTo(Bound b){
        assert(!Double.isNaN(value));
        assert(!Double.isNaN(b.value));
		//  a) [a a] (a 
		double result = value - b.value;
		if (Double.isNaN(result)){
		 // either both inf, or both -inf
		    result = 0.0;
		}
		assert(!Double.isNaN(result));
		if (value == b.value){ // take care of infinities
			if ((dir() == b.dir()) && (closed() == b.closed())){
				result = 0;
			} else if ( (isUpper() && open()) ||                     // a) ?
				 (b.isLower() && !b.closed()) ||                        // ?  (a
				 (closed() && b.closed() && isLower() && b.isUpper()) ){// [a a]
				// a) [a
				// a) a]
				// a) (a
				// [a (a
				// a] (a
				// [a a]
				result = -1; // correct order
			} else if ( (isLower() && open()) ||
					    (b.isUpper() && !b.closed()) ||
					    (closed && b.closed() && isUpper() && b.isLower()) ){
				// (a [a
				// (a a]
				// (a a)
				// a] a)
				// [a a)
				// a] [a
				result = 1; // reverse order
			}
		}
		return (int) Math.signum(result);
	}
	
	public Bound inverse(){
        assert((value != Double.POSITIVE_INFINITY) || !closed || isLower());
        assert((value != Double.NEGATIVE_INFINITY) || !closed || isUpper());
		return new Bound(value, dir.opposite(), !closed);
	}
//------------------------------------------------------------------------------
	@Override
	public boolean equals(Object o){
		boolean result = (o.getClass() == Interval.class);
		if (result){
			Interval i = (Interval) o;
			result = equals(i);
		}
		return result;
	}
	
	public boolean equals(Bound b){
		return ( (value == b.value) &&
				 (dir == b.dir) &&
				 (closed == b.closed) );
	}
	
	@Override
	public int hashCode(){
		Double v = value;
		return v.hashCode();
	}
	
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		if (isLower()){
			if (closed){
				sb.append("[");
			} else {
				sb.append("(");
			}
		}
		if (value == Double.NEGATIVE_INFINITY){
			sb.append("-inf");
		} else if (value == Double.POSITIVE_INFINITY){
			sb.append("inf");
		} else{
			sb.append(value);
		}
		if (isUpper()){
			if (closed){
				sb.append("]");
			} else {
				sb.append(")");
			}
		}
		return sb.toString();
	}
//------------------------------------------------------------------------------
	public static Bound min(Bound b1, Bound b2){
		Bound result = b1;
		if (b1.compareTo(b2) > 0){
			result = b2;
		}
		return result;
	}
	
	public static Bound max(Bound b1, Bound b2){
		Bound result = b2;
		if (b1.compareTo(b2) > 0){
			result = b1;
		}
		return result;
	}

}